/*
 * operations-common.h
 *
 *  Created on: Dec 1, 2015
 *      Author: zhangzm
 */

#ifndef OPERATIONS_COMMON_H_
#define OPERATIONS_COMMON_H_

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <inttypes.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/sendfile.h>
#include <openssl/sha.h>
#include <st.h>
#include <signal.h>
#include <sys/resource.h>

#include "heartbeat.h"
#include "protocol.h"
#include "backend-fs.h"
#include "incubator.h"
#include "networking.h"
#include "buffer.h"
#include "concurrent-connection-pool.h"
#include "master-request.h"
#include "netserver.h"
#include "thread-pool.h"
#include "operations.h"
#include "ursax.h"
#include "ursax-crc32.h"
#include "log.h"
#include "statistics.h"
#include "debug.h"
#include "ioengine.h"

typedef struct Write4_context
{
	union{
		struct Request_ChunkWriteReplicate4 req_w_rep;
		struct Request_ChunkWrite4 req_w;
	};
	union{
		struct Response_ChunkWriteReplicate4 resp_w_rep;
		struct Response_ChunkWrite4 resp_w;
	};
} __attribute__ ((packed)) Write4_context;

typedef struct MMapedBuf{
	char buf[MEMPOOL_BUFFER_SIZE];
	union{
		struct{
			int fd;
			Write4_context ctx;
			st_netfd_t stfd;
			struct Chunk* chunk;
		};
		char padding[4096*2];
	};
}__attribute__ ((packed)) MMapedBuf;
#define GET_BUF_FD(p) (((MMapedBuf*)p)->fd)
#define MEMPOOL_ALIGNMENT 512

#ifdef __cplusplus

typedef void (*defer_fun4)(void* arg1, void* arg2, void* arg3, void* arg4);
struct Defer {
	defer_fun4 fun4;
	void *arg1, *arg2, *arg3, *arg4;

	Defer(void* fun4=NULL, void* arg1=NULL, void* arg2=NULL, void* arg3=NULL, void* arg4=NULL) {
		this->fun4 = (defer_fun4)fun4;
		this->arg1 = arg1;
		this->arg2 = arg2;
		this->arg3 = arg3;
		this->arg4 = arg4;
	}
	~Defer() {
		if(fun4) {
			fun4(arg1, arg2, arg3, arg4);
		}
	}
};

template<typename R>
struct Defer0
{
	typedef R (*func_t)();
	func_t func;
	Defer0(func_t f)
	{
		func = f;
	}
	~Defer0()
	{
		if (func)
			func();
	}
};

template<typename R, typename T1>
struct Defer1
{
	typedef R (*func_t)(T1);
	func_t func;
	T1 arg1;
	Defer1(func_t f, T1 a1)
	{
		func = f;
		arg1 = a1;
	}
	~Defer1()
	{
		if (func)
			func(arg1);
	}
};

template<typename R, typename T1, typename T2>
struct Defer2
{
	typedef R (*func_t)(T1, T2);
	func_t func;
	T1 arg1;
	T2 arg2;
	Defer2(func_t f, T1 a1, T2 a2)
	{
		func = f;
		arg1 = a1;
		arg2 = a2;
	}
	~Defer2()
	{
		if (func)
			func(arg1, arg2);
	}
};

template<typename R, typename T1, typename T2, typename T3>
struct Defer3
{
	typedef R (*func_t)(T1, T2, T3);
	func_t func;
	T1 arg1;
	T2 arg2;
	T3 arg3;
	Defer3(func_t f, T1 a1, T2 a2, T3 a3)
	{
		func = f;
		arg1 = a1;
		arg2 = a2;
		arg3 = a3;
	}
	~Defer3()
	{
		if (func)
			func(arg1, arg2, arg3);
	}
};

template<typename R, typename T1, typename T2, typename T3, typename T4>
struct Defer4
{
	typedef R (*func_t)(T1, T2, T3, T4);
	func_t func;
	T1 arg1;
	T2 arg2;
	T3 arg3;
	T4 arg4;
	Defer4(func_t f, T1 a1, T2 a2, T3 a3, T4 a4)
	{
		func = f;
		arg1 = a1;
		arg2 = a2;
		arg3 = a3;
		arg4 = a4;
	}
	~Defer4()
	{
		if (func)
			func(arg1, arg2, arg3, arg4);
	}
};

struct ScopedIncrement
{
	uint64_t& x;
	ScopedIncrement(uint64_t& x_) : x(x_) { ++x; }
	~ScopedIncrement() { --x; }
};

struct AutoClose
{
	Chunk*& chunk;
	AutoClose(Chunk*& c) : chunk(c)	{ }
	~AutoClose() { be_chunk_close(chunk); }
};

struct AutoPut
{
	MemoryPool*& pool;
	void*& buf;
	AutoPut(MemoryPool*& p, void*& b) : pool(p), buf(b) { }
	~AutoPut()	{ pool->pfree(buf); buf = NULL; }
};

struct AutoPutArray
{
	MemoryPool*& pool;
	char **bufs;
	int n_bufs;
	AutoPutArray(MemoryPool*& p, char** b, int n) : pool(p), bufs(b), n_bufs(n) {
		for(int i = 0; i < n_bufs; i++){
			bufs[i] = (char*)pool->pmalloc(MMAPED_BUFFER_SIZE);
		}
	}
	~AutoPutArray() {
		for(int i = 0; i < n_bufs; i++){
			pool->pfree(bufs[i]);
			bufs[i] = NULL;
		}
	}
};

extern MemoryPool* mempool;
extern struct thread_pool* th_pool;

#endif

#define DECLARE_1BUF(buf)									\
		void* buf = mempool->pmalloc(MMAPED_BUFFER_SIZE);		\
		AutoPut __auto_put##buf(mempool, buf);

#define DECLARE_2BUF(buf1, buf2)					\
		DECLARE_1BUF(buf1);							\
		DECLARE_1BUF(buf2);

#define DECLARE_BUF_ARRAY(nb, bufs) 				\
		char *bufs[EC_MAX_M];						\
		AutoPutArray __auto_put_array##bufs(mempool, bufs, nb);

#define DECLARE_2BUF_ARRAY(nb1, bufs1, nb2, bufs2)	\
		DECLARE_BUF_ARRAY(nb1, bufs1);				\
		DECLARE_BUF_ARRAY(nb2, bufs2);

#define DECLARE_BUFS(nb, bufs, buf1, buf2)			\
		DECLARE_BUF_ARRAY(nb, bufs)					\
		DECLARE_2BUF(buf1, buf2)

#endif /* OPERATIONS_COMMON_H_ */
